widgetpaintable: Protect against too many signals
authorBenjamin Otte <otte@redhat.com>
Sun, 1 Apr 2018 13:25:16 +0000 (15:25 +0200)
committerBenjamin Otte <otte@redhat.com>
Thu, 5 Apr 2018 12:56:39 +0000 (14:56 +0200)
This is actually not just a mechnaism to protect against too many
signals, but it's also a method to getting those signals at the wrong
time.

For every size/content change, a widget needs to invalidate twice:
Once when it queues a resize/redraw (going valid => invalid) and once
when the new size/content is actually assigned (going invalid => valid).

However, one of those invalidations might be inconvenient for the
listener. GtkImage for example does not like receiving
invalidate-contents signals when new contents are assigned, but is fine
with them when the old ones go invalid. And it will not try to draw the
paintable in between anyway.

So by bypassing the 2nd emission if nothing was changed, we can make
GtkImage happy.

gtk/gtkwidget.c
gtk/gtkwidgetpaintable.c
gtk/gtkwidgetpaintableprivate.h [new file with mode: 0644]

index f22b4da99741e962ce8d5bae6251474b64e68381..2aad2c8fa573ef7971878f8fc602648ceff99953 100644 (file)
@@ -64,7 +64,7 @@
 #include "gtktooltipprivate.h"
 #include "gtktypebuiltins.h"
 #include "gtkversion.h"
-#include "gtkwidgetpaintable.h"
+#include "gtkwidgetpaintableprivate.h"
 #include "gtkwidgetpathprivate.h"
 #include "gtkwindowgroup.h"
 #include "gtkwindowprivate.h"
@@ -3978,7 +3978,7 @@ gtk_widget_invalidate_paintable_contents (GtkWidget *widget)
     return;
 
   for (l = priv->paintables; l; l = l->next)
-    gdk_paintable_invalidate_contents (l->data);
+    gtk_widget_paintable_invalidate_contents (l->data);
 }
 
 static void
@@ -3988,7 +3988,7 @@ gtk_widget_invalidate_paintable_size (GtkWidget *widget)
   GSList *l;
 
   for (l = priv->paintables; l; l = l->next)
-    gdk_paintable_invalidate_size (l->data);
+    gtk_widget_paintable_invalidate_size (l->data);
 }
 
 /**
@@ -4472,6 +4472,8 @@ gtk_widget_size_allocate (GtkWidget           *widget,
   priv->alloc_needed = FALSE;
   priv->alloc_needed_on_child = FALSE;
 
+  gtk_widget_invalidate_paintable_size (widget);
+
 check_clip:
   if (position_changed || size_changed || baseline_changed)
     gtk_widget_queue_draw (widget);
index be167d6005421021a40fb941c281972b41c1bf9a..948beb1b23bd5109db7859287679a59182d8e1b0 100644 (file)
@@ -19,7 +19,7 @@
 
 #include "config.h"
 
-#include "gtkwidgetpaintable.h"
+#include "gtkwidgetpaintableprivate.h"
 
 #include "gtkintl.h"
 #include "gtksnapshot.h"
@@ -59,6 +59,9 @@ struct _GtkWidgetPaintable
 
   GtkWidget *widget;
   guint loop_tracker;
+
+  guint size_invalid : 1;
+  guint contents_invalid : 1;
 };
 
 struct _GtkWidgetPaintableClass
@@ -84,6 +87,8 @@ gtk_widget_paintable_paintable_snapshot (GdkPaintable *paintable,
   GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (paintable);
   graphene_matrix_t transform;
 
+  self->contents_invalid = FALSE;
+
   if (self->widget == NULL ||
       !_gtk_widget_is_drawable (self->widget) ||
       _gtk_widget_get_alloc_needed (self->widget))
@@ -119,6 +124,11 @@ gtk_widget_paintable_paintable_snapshot (GdkPaintable *paintable,
 static GdkPaintable *
 gtk_widget_paintable_paintable_get_current_image (GdkPaintable *paintable)
 {
+  GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (paintable);
+
+  self->contents_invalid = FALSE;
+  self->size_invalid = FALSE;
+
   g_warning ("FIXME: Implement once we can create paintables from render nodes");
 
   return NULL;
@@ -129,6 +139,8 @@ gtk_widget_paintable_paintable_get_intrinsic_width (GdkPaintable *paintable)
 {
   GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (paintable);
 
+  self->size_invalid = FALSE;
+
   if (self->widget == NULL)
     return 0;
 
@@ -140,6 +152,8 @@ gtk_widget_paintable_paintable_get_intrinsic_height (GdkPaintable *paintable)
 {
   GtkWidgetPaintable *self = GTK_WIDGET_PAINTABLE (paintable);
 
+  self->size_invalid = FALSE;
+
   if (self->widget == NULL)
     return 0;
 
@@ -314,4 +328,22 @@ gtk_widget_paintable_set_widget (GtkWidgetPaintable *self,
   gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
 }
 
+void
+gtk_widget_paintable_invalidate_size (GtkWidgetPaintable *self)
+{
+  if (self->size_invalid)
+    return;
 
+  self->size_invalid = TRUE;
+  gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
+}
+
+void
+gtk_widget_paintable_invalidate_contents (GtkWidgetPaintable *self)
+{
+  if (self->contents_invalid)
+    return;
+
+  self->contents_invalid = TRUE;
+  gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
+}
diff --git a/gtk/gtkwidgetpaintableprivate.h b/gtk/gtkwidgetpaintableprivate.h
new file mode 100644 (file)
index 0000000..c5abc01
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#ifndef __GTK_WIDGET_PAINTABLE_PRIVATE_H__
+#define __GTK_WIDGET_PAINTABLE_PRIVATE_H__
+
+#include "gtkwidgetpaintable.h"
+
+
+void            gtk_widget_paintable_invalidate_size            (GtkWidgetPaintable     *self);
+void            gtk_widget_paintable_invalidate_contents        (GtkWidgetPaintable     *self);
+
+
+#endif /* __GTK_WIDGET_PAINTABLE_PRIVATE_H__ */